ThinkPHP-5.0.* RCE分析
前几周一直在复习考试,也没有看最近出的一些洞,寒假闲下来以后,要把最近出的洞都补了
漏洞核心
TP的这个RCE漏洞,主要的触发点在Request类中的method方法,这个存在一个当前类的任意方法调用,之后通过这个任意方法调用去覆盖掉filter的默认方法,从而实现RCE
可以看到在526行,调用了$this->{$this->method}($_POST)
,在这里,因为$this->method
和$_POST
我们都可控,可以调用当前类的任意方法,
漏洞分析
程序流程
- 在App.php中,在116行的
$dispatch = self::routeCheck($request,$config);
进入URL路由检测 - 通过URL检测,TP获取到执行的控制器为captcha
- 在以下的调用栈中获取到了请求方法的type为method
- 之后回到Route.php调用了$method = strtolower($request->method();这里就是触发点
- 进入$request->method()
这里如果传入的POST数组设置了配置中的var_method
的话,可以进入条件判断,我们看一下var_method
是什么值
所以,如果我们传入的POST数组中有_method
键值对的话就可以直接进入逻辑
- 进入逻辑后,很容易看出来,$this->method的值是我们的_method的值,我们可以利用这一点来执行当前类的任意方法
- 在当前类的
__construct()
方法中,传入的是一个数组,我们可以通过_method再一次调用这个类的__construct
函数
如果这个类没有可以用来进行一些恶意操作的函数的话,这个当前类的任意方法调用并没有什么用,但是巧的是,在__construct
函数中有着变量覆盖,这样的话,我们就可以覆盖整个类中的所有成员变量,我们看看__construct
函数
传入的参数是一个数组,正好我们之前的方法调用的参数正好是POST数组,而且在139行有一个很明显的变量赋值的操作,而且这个变量的键和值我们都可控,意味着我们可以进行当前类的变量覆盖,当然,显而易见的filter变量应该是最好的选择,因为filter会对所有传入的参数进行一次函数调用
最后,我们将filter的值覆盖为系统函数system
这里我们同时赋值$this->method的值为get,因为captcha路由规则需要是get方法才能不出错
- 在路由结束以后,调用了$data = self::exec($dispatch, $config);
- 因为在之前的路由中,判断路由的类型是method,所以进入了method的逻辑
- 这里又回到了Request类的方法调用,还记得我们之前对Reuqest类的变量覆盖吗,在这一步获取了filter的值,但是在之前,我们把filter覆盖为了system
- 在下面调用
array_walk_recursive
方法将$data的值都应用filterValue函数
- 最后,filterValue会对$data内的每个值都调用filter函数,这个时候,filter为system,data中为whoami
最后成功实现RCE
总结
TP的此次RCE是因为对传入的_method
没有进行过滤,所以TP在之后的修复中,也是对其中的_method
设置了白名单
现在的web漏洞已经不再是以前的一个SQL注入,命令执行打天下的时代了,现在web的漏洞很多都需要很长的调用链才可以将整个攻击连贯起来